﻿using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using ShrinkWrap.HttpHandler;
using Moq;
using System.Web;
using ShrinkWrap.Minifier;
using ShrinkWrap.Exceptions;
using ShrinkWrap.Test.Minifier;
using System.Linq.Expressions;
using System.IO;
using System.Web.Caching;

namespace ShrinkWrap.Test.HttpHandler
{
    [TestFixture]
    class AggregatorFixture
    {
        [TestCase(typeof(Aggregator), "http://www.example.com/path/foo.css+bar.config", ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(Aggregator), "http://www.example.com/path/foo.css+bar.css")]
        [TestCase(typeof(CachingAggregator), "http://www.example.com/path/foo.css+bar.config", ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(CachingAggregator), "http://www.example.com/path/foo.css+bar.css")]
        public void TestDifferingExtensionsException(Type type, string composite)
        {
            var builder = getAggregatorBuilder(type);
            builder(getEchoingMinifier(), new CompositeUri(composite), filename => "", int.MaxValue);
        }

        [TestCase(typeof(Aggregator), "http://www.example.com/path/foo.css+bar.css", 1, ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(Aggregator), "http://www.example.com/path/foo.css+bar.css+john.css", 2, ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(Aggregator), "http://www.example.com/path/foo.css+bar.css+john.css", 3)]
        [TestCase(typeof(CachingAggregator), "http://www.example.com/path/foo.css+bar.css", 1, ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(CachingAggregator), "http://www.example.com/path/foo.css+bar.css+john.css", 2, ExpectedException = typeof(ShrinkWrapException))]
        [TestCase(typeof(CachingAggregator), "http://www.example.com/path/foo.css+bar.css+john.css", 3)]
        public void TestExcessiveConstituentsException(Type type, string composite, int maxConstituents)
        {
            var builder = getAggregatorBuilder(type);
            builder(getEchoingMinifier(), new CompositeUri(composite), filename => "", maxConstituents);
        }

        /// <summary>
        /// Make a uniform psuedo-constructor for Aggregator subclasses so the same test case can be run
        /// unchanged on all subclasses.
        /// </summary>
        Func<IMinifier, CompositeUri, Func<String, String>, int, Aggregator> getAggregatorBuilder(Type aggregatorType)
        {
            if (aggregatorType == typeof(CachingAggregator))
            {
                return (min, uri, map, max) => new CachingAggregator(min, uri, map, max, HttpRuntime.Cache);
            }
            else if (aggregatorType == typeof(Aggregator))
            {
                return (min, uri, map, max) => new Aggregator(min, uri, map, max);
            }
            else
            {
                throw new ArgumentException("Unknown aggregator type.");
            }
        }

        /// <returns>An IMinifier that echoes its input.</returns>
        IMinifier getEchoingMinifier()
        {
            var echoer = new Mock<IMinifier>();
            echoer.Setup(e => e.Minify(It.IsAny<String>())).Returns((String s) => s);
            return echoer.Object;
        }

        /// <returns>An IMinifier that returns "" regardless of input.</returns>
        IMinifier getSilentMinifier()
        {
            var silent = new Mock<IMinifier>();
            silent.Setup(e => e.Minify(It.IsAny<String>())).Returns("");
            return silent.Object;
        }

        [TestCase(typeof(Aggregator), new String[] { "jquery-1.3.1" })]
        [TestCase(typeof(Aggregator), new String[] { "jquery-1.3.1", "mootools-1.2.3-core-nc" })]
        [TestCase(typeof(Aggregator), new String[] { "jquery-1.3.1", "mootools-1.2.3-core-nc", "jquery-1.3.1" })]
        [TestCase(typeof(CachingAggregator), new String[] { "jquery-1.3.1" })]
        [TestCase(typeof(CachingAggregator), new String[] { "jquery-1.3.1", "mootools-1.2.3-core-nc" })]
        [TestCase(typeof(CachingAggregator), new String[] { "jquery-1.3.1", "mootools-1.2.3-core-nc", "jquery-1.3.1" })]
        public void TestAggregation(Type aggregatorType, String[] filenames)
        {
            var builder = getAggregatorBuilder(aggregatorType);

            // Use a MinifierFixture to map names to test file locations
            var minifierFixture = new JavascriptMinifierFixture();
            var fullFilenames = filenames.Select<String, String>(minifierFixture.DataPathWithExtension);

            // Build the expected output
            var fileContents = fullFilenames.Select(name => File.ReadAllText(name));
            var compositeUri = buildCompositeUri(filenames, ".js");
            var expected = aggregate(fileContents);

            var aggregator = builder(getEchoingMinifier(), compositeUri, minifierFixture.DataPath, int.MaxValue);

            Assert.AreEqual(expected, aggregator.GetAggregate());
        }

        [TestCase(typeof(Aggregator), new String[] { "blueprint-grid" })]
        [TestCase(typeof(Aggregator), new String[] { "blueprint-grid", "blueprint-forms" })]
        [TestCase(typeof(Aggregator), new String[] { "blueprint-grid", "blueprint-forms", "blueprint-grid" })]
        [TestCase(typeof(CachingAggregator), new String[] { "blueprint-grid" })]
        [TestCase(typeof(CachingAggregator), new String[] { "blueprint-grid", "blueprint-forms" })]
        [TestCase(typeof(CachingAggregator), new String[] { "blueprint-grid", "blueprint-forms", "blueprint-grid" })]
        public void TestFraming(Type aggregatorType, String[] filenames)
        {
            var builder = getAggregatorBuilder(aggregatorType);

            // Use a MinifierFixture to map names to test file locations
            var minifierFixture = new CssMinifierFixture();
            var fullFilenames = filenames.Select<String, String>(minifierFixture.DataPathWithExtension);

            // Build the expected output
            var emptyFileContents = fullFilenames.Select(name => "");
            var compositeUri = buildCompositeUri(filenames, ".css");
            var expected = aggregate(emptyFileContents);

            var aggregator = builder(getSilentMinifier(), compositeUri, minifierFixture.DataPath, int.MaxValue);

            Assert.AreEqual(expected, aggregator.GetAggregate());
        }

        private static CompositeUri buildCompositeUri(IEnumerable<String> filenames, String extension)
        {
            // Build a Composite Uri from the input filenames
            var filenamesWithExtension = filenames.Select(n => n + extension);
            String compositeString = "http://example.com/" + filenamesWithExtension.Aggregate((x, y) => x + "+" + y);
            var compositeUri = new CompositeUri(compositeString);
            return compositeUri;
        }

        private String aggregate(IEnumerable<String> minifieds)
        {
            return minifieds.Aggregate((agg, min) => agg + "\n" + min);
        }
    }
}
